
#include <geometrypool.h>
#include <sceneloader.h>
#include <session.h>
#include <manager/meshmanager.h>
#include <QFile>

SceneLoader::SceneLoader(ceEngine* engine)
	: _engine (engine)
{
}

Geometry* SceneLoader::LoadGeometry(QDomElement element)
{
  Geometry* geom = GeometryPool::Get()->GetGeometry(_geometryName, _geometryIdx);
  if (geom)
    {
      return geom;
    }

  if (element.nodeName() != QString ("geometry"))
    {
      printf ("ceXMLDataLoader::LoadGeometry: Illegal data format: '%s' != 'geometry'\n", element.nodeName().toStdString().c_str()); fflush (stdout);
      return 0;
    }

  geom = new Geometry ();

  QDomElement staticElement = element.firstChildElement("staticmesh");
  if (!staticElement.isNull())
    {
      ceMesh* mesh = LoadMesh (staticElement);
      if (!mesh)
        {
          geom->Release();
          return 0;
        }
      iStaticMesh* staticMesh = _engine->GetDevice()->CreateStaticMesh();
      staticMesh->SetMesh(mesh);
      geom->SetMesh(staticMesh);
      geom->SetMetaMesh(MT_Static,
      staticElement.firstChildElement("mesh").attribute("filename"));
    }

  MaterialManager* matMgr = Session::Get()->GetMaterialManager();

  QDomElement materialElement = element.firstChildElement("material");
  Material* mat = 0;
  if (!materialElement.isNull())
    {
      mat = matMgr->GetMaterial(materialElement.text());
    }

  if (!mat)
    {
      mat = matMgr->GetDefaultMaterial();
    }
  geom->SetMetaMaterial(mat);

  GeometryPool::Get()->Set(_geometryName, _geometryIdx, geom);
  return geom;

}


ceMesh* SceneLoader::LoadMesh(QDomElement element)
{
  QDomElement meshElement = element.firstChildElement("mesh");
  if (meshElement.isNull())
    {
      return 0;
    }

  QString filename = meshElement.attribute("filename");
  if (filename.isNull())
    {
      return 0;
    }


  return ceMeshManager::Get()->GetMesh(filename.toStdString());
}


EntityNode* SceneLoader::LoadEntityNode(QDomElement element)
{
  if (element.nodeName() != QString ("node") ||
      QString::compare(element.attribute("type"), "entity", Qt::CaseInsensitive) != 0)
    {
      printf ("ceXMLDataLoader::LoadEntityNode: Illegal data format: '%s' != 'entitynode'\n", element.nodeName().toStdString().c_str()); fflush (stdout);
      return 0;
    }

  EntityNode* entity = new EntityNode();
  QString  name = element.attribute("name");
  if (!name.isNull())
    {
      entity->SetName(name.toStdString());
    }


  QDomElement geometryElement = element.firstChildElement("geometry");
  if (!geometryElement.isNull())
    {
      Geometry* geom = LoadGeometry(geometryElement);
      if (geom)
        {
          _geometryIdx++;
        }
      entity->SetGeometry(geom);
    }

  QDomElement shadowElement = element.firstChildElement("shadow");
  if (!shadowElement.isNull())
    {
      LoadShadow (entity, shadowElement);
    }
  entity->SetCastShadow(true);

  QDomElement transformElement = element.firstChildElement("transformation");
  if (!transformElement.isNull())
    {
      QDomElement matrixElement = transformElement.firstChildElement("matrix");
      entity->SetMatrix(LoadMatrix4f(matrixElement));
      entity->FinishTransformation(false);
    }

  return entity;
}



void SceneLoader::LoadShadow (EntityNode* entity, QDomElement element)
{
  if (!entity || element.isNull())
    {
      return;
    }

  bool val = false;
  if (element.hasAttribute("castShadow"))
    {
      QString csv = element.attribute("castShadow");
      if (csv.compare("true", Qt::CaseInsensitive))
        {
          val = true;
        }
    }

  entity->SetCastShadow(val);
}


ceMatrix4f SceneLoader::LoadMatrix4f (QDomElement element)
{
  ceMatrix4f M;
  if (element.nodeName() != QString("matrix"))
    {
      printf ("ceXMLDataLoader::LoadMatrix4f: Illegal data format: '%s' != 'matrix'\n", element.nodeName().toStdString().c_str()); fflush (stdout);
      return M;
    }
#define RF(name, dst) if (re.hasAttribute(name)) dst = re.attribute(name).toFloat()

  for (QDomElement re = element.firstChildElement("row");
       !re.isNull();
       re = re.nextSiblingElement("row"))
    {
      RF("m00", M._00);
      RF("m01", M._01);
      RF("m02", M._02);
      RF("m03", M._03);

      RF("m10", M._10);
      RF("m11", M._11);
      RF("m12", M._12);
      RF("m13", M._13);

      RF("m20", M._20);
      RF("m21", M._21);
      RF("m22", M._22);
      RF("m23", M._23);

      RF("m30", M._30);
      RF("m31", M._31);
      RF("m32", M._32);
      RF("m33", M._33);
    }
#undef RF
  return M;
}

QString SceneLoader::LoadName(const QString &filename)
{
  QFile file (filename);
  if (!file.open(QIODevice::ReadOnly))
    {
      return 0;
    }

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return 0;
    }
  file.close();

  return LoadName (doc.documentElement());
}

QString SceneLoader::LoadName(QDomElement element)
{
  if (!element.hasAttribute("name"))
    {
      return "";
    }

  return element.attribute("name");
}

GroupNode* SceneLoader::LoadGroupNode(const QString &filename)
{
  QFile file (filename);
  if (!file.open(QIODevice::ReadOnly))
    {
      return 0;
    }
  _geometryName = filename;
  _geometryIdx = 0;

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return 0;
    }
  file.close();

  return LoadGroupNode(doc.documentElement());
}


GroupNode* SceneLoader::LoadGroupNode(QDomElement element)
{
  if (element.nodeName() != QString ("node") ||
      QString::compare(element.attribute("type"), "group", Qt::CaseInsensitive) != 0)
    {
      printf ("ceXMLDataLoader::LoadGroupNode: Illegal data format: '%s' != 'group'\n", element.nodeName().toStdString().c_str()); fflush (stdout);
    }

  GroupNode* group = new GroupNode();

  QString name = element.attribute("name");
  if (!name.isNull())
    {
      group->SetName(name.toStdString());
    }

  for (QDomElement groupElement = element.firstChildElement();
       !groupElement.isNull();
       groupElement = groupElement.nextSiblingElement())

    {
      ceNode* node = LoadNode (groupElement);
      if (node)
        {
          group->AddNode(node);
        }
    }

  group->UpdateBoundingBox();
  return group;
}

LODNode* SceneLoader::LoadLODNode(QDomElement element)
{
  if (element.nodeName() != QString ("node") ||
      QString::compare(element.attribute("type"), "lod", Qt::CaseInsensitive) != 0)
    {
      printf ("ceXMLDataLoader::LoadLODNode: Illegal data format: '%s' != 'group'\n", element.attribute("type").toStdString().c_str()); fflush (stdout);
    }

  LODNode* lod = new LODNode ();

  QString name = element.attribute("name");
  if (!name.isNull())
    {
      lod->SetName(name.toStdString());
    }

  for (QDomElement groupElement = element.firstChildElement();
       !groupElement.isNull();
       groupElement = groupElement.nextSiblingElement())

    {
      ceNode* node = LoadNode (groupElement);
      if (node)
        {
          lod->Get().Add(node);
        }
    }
  lod->GetSolver()->SetNum(lod->Get().GetNumberOfLevels());

  lod->UpdateBoundingBox();
  return lod;
}

ceNode* SceneLoader::LoadNode(QDomElement element)
{
  if (element.nodeName() != "node" ||
      !element.hasAttribute("type"))
    {
      return 0;
    }

  QString type = element.attribute("type");
  if (type.compare("entity", Qt::CaseInsensitive) == 0)
    {
      return LoadEntityNode(element);
    }
  else if (type.compare("group", Qt::CaseInsensitive) == 0)
    {
      return LoadGroupNode(element);
    }
  else if (type.compare("lod", Qt::CaseInsensitive) == 0)
    {
      return LoadLODNode (element);
    }

  return 0;
}


ceNode* SceneLoader::Load(const QString &filename)
{
  QFile file (filename);
  if (!file.open(QIODevice::ReadOnly))
    {
      return 0;
    }
  _geometryName = filename;
  _geometryIdx = 0;

  QDomDocument doc;
  if (!doc.setContent(&file))
    {
      file.close();
      return 0;
    }
  file.close();

  return Load(doc.documentElement());
}

ceNode* SceneLoader::Load(QDomElement element)
{
  if (element.nodeName() != "node")
    {
      return 0;
    }
  return LoadNode (element);
}
